package com.apobates.forum.thrones.controller;

import com.apobates.forum.event.elderly.ForumActionEnum;
import com.apobates.forum.letterbox.entity.ForumLetter;
import com.apobates.forum.letterbox.entity.ForumLetterReceiver;
import com.apobates.forum.letterbox.entity.ForumLetterTypeEnum;
import com.apobates.forum.letterbox.entity.proxy.ForumLetterReplica;
import com.apobates.forum.letterbox.service.ForumLetterService;
import com.apobates.forum.letterbox.service.InboxService;
import com.apobates.forum.letterbox.service.OutboxService;
import com.apobates.forum.member.entity.Member;
import com.apobates.forum.member.service.MemberService;
import com.apobates.forum.member.storage.core.MemberSessionBean;
import com.apobates.forum.thrones.controller.form.ForumMessageForm;
import com.apobates.forum.thrones.controller.helper.OnlineDescriptor;
import com.apobates.forum.thrones.vo.MessageBodyDigest;
import com.apobates.forum.thrones.vo.MutualLetter;
import com.apobates.forum.utils.lang.CommonBean;
import com.apobates.forum.utils.Commons;
import com.apobates.forum.utils.DateTimeUtils;
import com.apobates.forum.utils.FrontPageURL;
import com.apobates.forum.utils.TipMessage;
import com.apobates.forum.utils.lang.EnumArchitecture;
import com.apobates.forum.utils.persistence.Page;
import com.apobates.forum.utils.persistence.PageRequest;
import com.apobates.forum.utils.persistence.Pageable;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 消息控制器
 *
 * @author xiaofanku
 * @since 20200511
 */
@Controller
@RequestMapping(value = "/message")
public class MessageController {
    @Autowired
    private InboxService inboxService;
    @Autowired
    private OutboxService outboxService;
    @Autowired
    private ForumLetterService forumLetterService;
    @Autowired
    private MemberService memberService;
    private final static Logger logger = LoggerFactory.getLogger(MessageController.class);
    
    //与我通消息的会员[A]
    @GetMapping(path = "/")
    @OnlineDescriptor(action=ForumActionEnum.MESSAGE_BOX)
    public String homePage(
            MemberSessionBean mbean,
            HttpServletRequest request,
            Model model) {
        //未读的消息数量
        model.addAttribute("unreadSize", inboxService.countForMemberMessages(mbean.getMid()));
        return "default/message/index";
    }
    
    @GetMapping(path = "/inbox/rawdata", produces = "application/json;charset=UTF-8")
    @ResponseBody
    public String inboxRawdata(
            @RequestParam("pageNumber") int page,
            @RequestParam("pageSize") int pageSize,
            MemberSessionBean mbean,
            HttpServletRequest request,
            Model model) {
        Page<ForumLetter> rs = inboxService.getInBoxGroupSender(mbean.getMid(), new PageRequest(page, pageSize));
        //
        Map<String, Object> result = Map.ofEntries(Map.entry("result", rs.getResult().map(InboxClassVO::new).collect(Collectors.toList())), Map.entry("total", rs.getTotalElements()), Map.entry("page", page), Map.entry("size", pageSize));
        return new Gson().toJson(result);
    }
    
    // [A]{/message/}列表中未读消息数量
    @GetMapping(path = "/size", produces = "application/json;charset=UTF-8")
    @ResponseBody
    public List<CommonBean> getUnReadMessageSize(@RequestParam("sender") String senderIdString, MemberSessionBean mbean, HttpServletRequest request, Model model) {
        Set<Long> senderIdSet = Commons.toLongSet(senderIdString);
        Map<Long, Long> rs = inboxService.groupForMemberMessages(mbean.getMid(), senderIdSet);
        //
        return rs.entrySet().stream().map((entry)->new CommonBean(entry.getKey(), entry.getValue() + "")).collect(Collectors.toList());
    }
    
    //发件箱
    @GetMapping(path = "/sent")
    @OnlineDescriptor(action=ForumActionEnum.MESSAGE_SENT)
    public String sendPage(
            @RequestParam(value = "type", required = false, defaultValue = "0") int typeEnumSymbol,
            MemberSessionBean mbean,
            HttpServletRequest request,
            Model model) {
        ForumLetterTypeEnum label = EnumArchitecture.getInstance(typeEnumSymbol, ForumLetterTypeEnum.class).orElse(ForumLetterTypeEnum.ALL);
        long count = (ForumLetterTypeEnum.ALL == label) ? outboxService.countSentByMember(mbean.getMid()) : outboxService.countSentByMember(mbean.getMid(), label);
        model.addAttribute("total", count);
        return "default/message/sent";
    }
    
    @GetMapping(path = "/outbox/rawdata", produces = "application/json;charset=UTF-8")
    @ResponseBody
    public String getMemberSendRawdata(
            @RequestParam(value = "type", required = false, defaultValue = "0") int typeEnumSymbol,
            @RequestParam("pageNumber") int page,
            @RequestParam("pageSize") int pageSize,
            MemberSessionBean mbean,
            HttpServletRequest request,
            Model model) {
        ForumLetterTypeEnum label = EnumArchitecture.getInstance(typeEnumSymbol, ForumLetterTypeEnum.class).orElse(ForumLetterTypeEnum.ALL);
        Pageable pr = new PageRequest(page, pageSize);
        Page<ForumLetterReplica> rs = (ForumLetterTypeEnum.ALL == label) ? outboxService.getSent(mbean.getMid(), pr) : outboxService.getSent(mbean.getMid(), label, pr);
        //
        Map<String, Object> result = Map.ofEntries(Map.entry("result", rs.getResult().map(OutboxClassVO::new).collect(Collectors.toList())), Map.entry("total", rs.getTotalElements()), Map.entry("page", page), Map.entry("size", pageSize));
        return new Gson().toJson(result);
    }
    
    //新消息
    @GetMapping(path = "/create")
    @OnlineDescriptor(action=ForumActionEnum.MESSAGE_CREATE)
    public String createForm(
            @RequestParam(value = "receiver", required = false, defaultValue = "0") long receiver,
            @RequestParam(value = "names", required = false, defaultValue = "") String names,
            @RequestParam(value = "token", required = false, defaultValue = "0") String token,
            MemberSessionBean mbean,
            HttpServletRequest request,
            Model model) {
        ForumMessageForm form = new ForumMessageForm();
        if (receiver > 0) {
            form.setUid("u" + receiver);
        }
        form.setSnames(names);
        form.setToken(token);
        model.addAttribute("form", form);
        return "default/message/create";
    }
    @PostMapping(path = "/create")
    @OnlineDescriptor(action=ForumActionEnum.MESSAGE_CREATE)
    public String createAction(
            HttpServletRequest request,
            MemberSessionBean mbean,
            @Valid @ModelAttribute("form") ForumMessageForm form,
            BindingResult bindingResult,
            Model model) {
        if (bindingResult.hasErrors()) {
            model.addAttribute("form", form);
            return "default/message/create";
        }
        Member m = memberService.get(form.getMemberId()).orElse(null);
        //
        if (null != m && Commons.isNotBlank(form.getContent())) {
            //发布消息
            String mt = Commons.isNotBlank(form.getTitle()) ? form.getTitle() : mbean.getNickname() + "发来一封私信";
            long fmId = forumLetterService.create(mt, form.getContent(), m.getId(), m.getNickname(), mbean.getMid(), mbean.getNickname());
            if (fmId > 0) {
                return "redirect:/message/sent";
            }
            model.addAttribute("form", form);
            model.addAttribute("errors", "发送失败, 建议您稍候重新尝试");
            return "default/message/create";
        }
        model.addAttribute("form", form);
        model.addAttribute("errors", "发送失败, 建议您检查是否有漏填的输入");
        return "default/message/create";
    }
    
    //ajax站内信
    @PostMapping(path = "/transmit", produces = "application/json;charset=UTF-8")
    @ResponseBody
    @OnlineDescriptor(action=ForumActionEnum.MESSAGE_CREATE)
    public MessageBodyDigest transmitMessageAction(
            @RequestParam("receiver") long receiver,
            @RequestParam("names") String receiverNickname,
            @RequestParam("content") String content,
            @RequestParam(value = "token", required = false, defaultValue = "0") String token,
            MemberSessionBean mbean,
            HttpServletRequest request,
            Model model) {
        if (receiver < 1) {
            return MessageBodyDigest.ofEmpty("未知的消息接受者");
        }
        String letterTitle = String.format("%s发来一封私信", mbean.getNickname());
        Optional<ForumLetterReplica> fm = forumLetterService.build(letterTitle, content.trim(), receiver, receiverNickname, mbean.getMid(), mbean.getNickname());
        if (fm.isPresent() && fm.get().getId() > 0) {
            return new MessageBodyDigest(fm.get(), receiver);
        }
        return new MessageBodyDigest(mbean.getMid(), mbean.getNickname(), receiver, letterTitle, content);
    }
    
    //收件人将消息标为已读
    @PostMapping(path = "/read", produces = "application/json;charset=UTF-8")
    @ResponseBody
    @OnlineDescriptor(action=ForumActionEnum.MESSAGE_ACTION_READ)
    public TipMessage readMessageAction(
            @RequestParam("ids") String idString,
            @RequestParam(value = "token", required = false, defaultValue = "0") String token,
            MemberSessionBean mbean,
            HttpServletRequest request,
            Model model) {
        List<Long> messageIdList = Commons.toLongList(idString);
        return TipMessage.Builder.of(()->inboxService.readed(mbean.getMid(), messageIdList)>0).success("标记成功").error("标记为阅读操作失败");
    }
    
    //收件人将消息标为删除
    @PostMapping(path = "/delete", produces = "application/json;charset=UTF-8")
    @ResponseBody
    @OnlineDescriptor(action=ForumActionEnum.MESSAGE_ACTION_DEL)
    public TipMessage deleteMessageAction(
            @RequestParam("ids") String idString,
            @RequestParam(value = "token", required = false, defaultValue = "0") String token,
            MemberSessionBean mbean,
            HttpServletRequest request,
            Model model) {
        List<Long> messageIdList = Commons.toLongList(idString);
        return TipMessage.Builder.of(()->inboxService.remove(mbean.getMid(), messageIdList)>0).success("删除成功").error("删除消息操作失败");
    }
    
    //收件箱中的查看
    @GetMapping(path = "/view")
    @OnlineDescriptor(action=ForumActionEnum.MESSAGE_BROWSE)
    public String showMessage(
            @RequestParam("sender") long sendMemberId,
            @RequestParam(value = "pn", required = false, defaultValue = "5") int pageSize,
            @RequestParam(value = "token", required = false, defaultValue = "0") String token,
            MemberSessionBean mbean,
            HttpServletRequest request,
            Model model) {
        //我与发件人来往的信件
        FrontPageURL fpbuild = new FrontPageURL(request.getContextPath() + "/message/view").addPageSize("number", pageSize).addParameter("sender", sendMemberId);
        //打开这个页面永远只显示第一页,此页面无分页区
        Pageable pr = new PageRequest(1, fpbuild.getPageSize());
        //保证是降序
        Page<ForumLetter> data = forumLetterService.getMutualLetter(sendMemberId, mbean.getMid(), pr);
        //jsp中foreach,需要将之重排成升序
        List<ForumLetter> rs = data.getResult().sorted(Comparator.comparing(ForumLetter::getEntryDateTime)).collect(Collectors.toList());
        //主在右，客在左
        model.addAttribute("rs", rs);
        model.addAttribute("master", mbean.toMember());
        //
        boolean hasMoreRecords = data.getTotalElements() > pageSize;
        model.addAttribute("hasMore", hasMoreRecords);
        //可能这一页只有主,没有发送者信息
        Member senderMember = memberService.get(sendMemberId).orElse(Member.empty(sendMemberId));
        model.addAttribute("senderNickname", senderMember.getNickname());
        model.addAttribute("sender", sendMemberId);
        model.addAttribute("token", token); //Commons.randomAlphaNumeric(8)
        return "default/message/inbox_chat"; //
    }
    
    @GetMapping(path = "/view/more", produces = "application/json;charset=UTF-8")
    @ResponseBody
    public String getMutualHistoryLetter(
            @RequestParam("sender") long sendMemberId,
            @RequestParam("p") int page,
            @RequestParam(value = "pn", required = false, defaultValue = "5") int pageSize,
            MemberSessionBean mbean,
            HttpServletRequest request,
            Model model) {
        Pageable pr = new PageRequest(page, pageSize);
        Page<ForumLetter> data = forumLetterService.getMutualLetter(sendMemberId, mbean.getMid(), pr);
        //保证是降序,push到js中是第一个先填充
        List<MessageBodyDigest> entries = data.getResult()
                .map(fl -> {
                    boolean isMaster = fl.getAuthor() == mbean.getMid(); //信的作者=当前用户
                    long receiverMember = (isMaster) ? sendMemberId : mbean.getMid();
                    return new MessageBodyDigest(fl, receiverMember, isMaster);
                })
                .collect(Collectors.toList());
        //
        boolean hasMoreRecords = data.getTotalElements() > (page * pageSize);
        MutualLetter ml = new MutualLetter(sendMemberId, mbean.getMid(), page, pageSize, hasMoreRecords);
        //
        Gson gson = new Gson();
        Type setType = new TypeToken<TreeSet<MessageBodyDigest>>() {}.getType();
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("letter", gson.toJson(ml));
        jsonObject.add("result", gson.toJsonTree(entries, setType));
        return jsonObject.toString();
    }
    
    //全部标记为已读
    //将发件人发送的消息全部标记为阅读
    @PostMapping(path = "/read/all", produces = "application/json;charset=UTF-8")
    @ResponseBody
    @OnlineDescriptor(action=ForumActionEnum.MESSAGE_ACTION_READ)
    public TipMessage readAllMessageAction(
            @RequestParam("member") long memberId,
            @RequestParam("direction") int direction,
            @RequestParam(value = "token", required = false, defaultValue = "0") String token,
            MemberSessionBean mbean,
            HttpServletRequest request,
            Model model) {
        final long othMember =(direction == 1)?memberId:-1;
        return TipMessage.Builder.take(()->inboxService.compositeReaded(mbean.getMid(), othMember, direction)).success("标记成功").error("标记为阅读操作失败");
    }
    
    //今天的公告
    @GetMapping(path = "/notice", produces = "application/json;charset=UTF-8")
    @ResponseBody
    public List<Map<String, String>> getTodayClaimLetter(
            HttpServletRequest request,
            Model model) {
        Function<ForumLetter, Map<String, String>> mapper = fl->{
            Map<String, String> tmp = new HashMap<>();
            tmp.put("title", fl.getTitle());
            tmp.put("content", fl.getContent());
            tmp.put("author", fl.getAuthor() + "");
            tmp.put("nickname", fl.getNickname());
            tmp.put("pubdate", DateTimeUtils.formatClock(fl.getEntryDateTime()));
            return tmp;
        };
        return forumLetterService.getTodayClaim().map(mapper).collect(Collectors.toList());
    }
    
    class InboxClassVO {
        private final String subject;
        private final long sender;
        private final String senderNickname;
        private final String senderURI;
        private final String datetime;
        private final boolean robot;
        
        public InboxClassVO(ForumLetter letter) {
            this.subject = letter.getTitle();
            this.sender = letter.getAuthor();
            this.senderNickname = letter.getNickname();
            if (0 == letter.getAuthor()) {
                this.senderURI = "";
                this.robot = true;
            } else {
                this.senderURI = String.format("/member/%d.xhtml", letter.getAuthor());
                this.robot = false;
            }
            this.datetime = DateTimeUtils.formatClock(letter.getEntryDateTime());
        }
        
        public String getSubject() {
            return subject;
        }
        
        public long getSender() {
            return sender;
        }
        
        public String getSenderNickname() {
            return senderNickname;
        }
        
        public String getSenderURI() {
            return senderURI;
        }
        
        public String getDatetime() {
            return datetime;
        }
        
        public boolean isRobot() {
            return robot;
        }
    }
    
    class OutboxClassVO {
        private final String subject;
        private final String datetime;
        private final String content;
        private final String category;
        private final List<Map<String, String>> receives;
        private final boolean everyone;
        //多个中的一位
        private final long receiver;
        private final String receiverURI;
        private final String receiverNickname;
        
        public OutboxClassVO(ForumLetterReplica letter) {
            this.subject = letter.getTitle();
            this.content = letter.getContent();
            this.datetime = DateTimeUtils.formatClock(letter.getEntryDateTime());
            this.category = letter.getTyped().getTitle();
            //
            List<Map<String, String>> reces = new ArrayList<>();
            boolean isEveryOne = false;
            for (ForumLetterReceiver flr : letter.getReceivers()) {
                Map<String, String> tmp = new HashMap<>();
                tmp.put("nickname", flr.getMemberNickname());
                tmp.put("id", flr.getMember() + "");
                reces.add(tmp);
                if (!isEveryOne && 0L == flr.getMember()) {
                    isEveryOne = true;
                }
            }
            this.receives = reces;
            this.everyone = isEveryOne;
            //
            if(!reces.isEmpty()){
                this.receiver = Long.valueOf(reces.get(0).get("id"));
                this.receiverNickname = reces.get(0).get("nickname");
            }else{
                this.receiver =0L;
                this.receiverNickname="-";
            }
            this.receiverURI = String.format("/member/%d.xhtml", receiver);
        }
        
        public String getSubject() {
            return subject;
        }
        
        public String getDatetime() {
            return datetime;
        }
        
        public String getContent() {
            return content;
        }
        
        public String getCategory() {
            return category;
        }
        
        public List<Map<String, String>> getReceives() {
            return receives;
        }
        
        public boolean isEveryone() {
            return everyone;
        }
        
        public long getReceiver() {
            return receiver;
        }
        
        public String getReceiverURI() {
            return receiverURI;
        }
        
        public String getReceiverNickname() {
            return receiverNickname;
        }
    }
}